import Event from './Event'
import classes from 'component-classes'
import { requestAnimationTimeout, cancelAnimationTimeout } from '../requestAnimationTimeout'

const isCssAnimationSupported = Event.endEvents.length !== 0
const capitalPrefixes = ['Webkit',
  'Moz',
  'O',
  // ms is special .... !
  'ms']
const prefixes = ['-webkit-', '-moz-', '-o-', 'ms-', '']

function getStyleProperty (node, name) {
  // old ff need null, https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle
  const style = window.getComputedStyle(node, null)
  let ret = ''
  for (let i = 0; i < prefixes.length; i++) {
    ret = style.getPropertyValue(prefixes[i] + name)
    if (ret) {
      break
    }
  }
  return (ret)
}

function fixBrowserByTimeout (node) {
  if (isCssAnimationSupported) {
    const transitionDelay = parseFloat(getStyleProperty(node, 'transition-delay')) || 0
    const transitionDuration = parseFloat(getStyleProperty(node, 'transition-duration')) || 0
    const animationDelay = parseFloat(getStyleProperty(node, 'animation-delay')) || 0
    const animationDuration = parseFloat(getStyleProperty(node, 'animation-duration')) || 0
    const time = Math.max(transitionDuration + transitionDelay, animationDuration + animationDelay)
    // sometimes, browser bug
    node.rcEndAnimTimeout = setTimeout(() => {
      node.rcEndAnimTimeout = null
      if (node.rcEndListener) {
        node.rcEndListener()
      }
    }, (time) * 1000 + 200)
  }
}

function clearBrowserBugTimeout (node) {
  if (node.rcEndAnimTimeout) {
    clearTimeout(node.rcEndAnimTimeout)
    node.rcEndAnimTimeout = null
  }
}

const cssAnimation = (node, transitionName, endCallback) => {
  const nameIsObj = typeof transitionName === 'object'
  const className = nameIsObj ? transitionName.name : transitionName
  const activeClassName = nameIsObj ? transitionName.active : `${transitionName}-active`
  let end = endCallback
  let start
  let active
  const nodeClasses = classes(node)

  if (endCallback && Object.prototype.toString.call(endCallback) === '[object Object]') {
    end = endCallback.end
    start = endCallback.start
    active = endCallback.active
  }

  if (node.rcEndListener) {
    node.rcEndListener()
  }

  node.rcEndListener = (e) => {
    if (e && e.target !== node) {
      return
    }

    if (node.rcAnimTimeout) {
      cancelAnimationTimeout(node.rcAnimTimeout)
      node.rcAnimTimeout = null
    }

    clearBrowserBugTimeout(node)

    nodeClasses.remove(className)
    nodeClasses.remove(activeClassName)

    Event.removeEndEventListener(node, node.rcEndListener)
    node.rcEndListener = null

    // Usually this optional end is used for informing an owner of
    // a leave animation and telling it to remove the child.
    if (end) {
      end()
    }
  }

  Event.addEndEventListener(node, node.rcEndListener)

  if (start) {
    start()
  }
  nodeClasses.add(className)

  node.rcAnimTimeout = requestAnimationTimeout(() => {
    node.rcAnimTimeout = null
    nodeClasses.add(activeClassName)
    if (active) {
      requestAnimationTimeout(active, 0)
    }
    fixBrowserByTimeout(node)
    // 30ms for firefox
  }, 30)

  return {
    stop () {
      if (node.rcEndListener) {
        node.rcEndListener()
      }
    },
  }
}

cssAnimation.style = (node, style, callback) => {
  if (node.rcEndListener) {
    node.rcEndListener()
  }

  node.rcEndListener = (e) => {
    if (e && e.target !== node) {
      return
    }

    if (node.rcAnimTimeout) {
      cancelAnimationTimeout(node.rcAnimTimeout)
      node.rcAnimTimeout = null
    }

    clearBrowserBugTimeout(node)

    Event.removeEndEventListener(node, node.rcEndListener)
    node.rcEndListener = null

    // Usually this optional callback is used for informing an owner of
    // a leave animation and telling it to remove the child.
    if (callback) {
      callback()
    }
  }

  Event.addEndEventListener(node, node.rcEndListener)

  node.rcAnimTimeout = requestAnimationTimeout(() => {
    for (const s in style) {
      if (style.hasOwnProperty(s)) {
        node.style[s] = style[s]
      }
    }
    node.rcAnimTimeout = null
    fixBrowserByTimeout(node)
  }, 0)
}

cssAnimation.setTransition = (node, p, value) => {
  let property = p
  let v = value
  if (value === undefined) {
    v = property
    property = ''
  }
  property = property || ''
  capitalPrefixes.forEach((prefix) => {
    node.style[`${prefix}Transition${property}`] = v
  })
}

cssAnimation.isCssAnimationSupported = isCssAnimationSupported

export {
  isCssAnimationSupported,
}

export default cssAnimation
